package com.lord.otter.component.infrastructure.retry.domain;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import com.lord.otter.component.infrastructure.retry.constant.ExecModel;
import com.lord.otter.component.infrastructure.retry.constant.ExecStatus;
import com.lord.otter.component.infrastructure.retry.constant.HttpMethod;
import com.lord.otter.component.infrastructure.retry.domain.command.AddHttpRetryTaskCommand;
import lombok.Data;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * http任务
 *
 * @author lord
 * 2025/8/8 17:30
 */
@Data
public class HttpRetryTask {
    /**
     * 任务id
     */
    private Long id;

    /**
     * 请求url
     */
    private String url;

    /**
     * 请求方法
     */
    private HttpMethod method;

    /**
     * 请求体
     */
    private String requestBody;

    /**
     * 请求头
     */
    private String headers;

    /**
     * 请求结果
     */
    private String responseBody;

    /**
     * 执行次数
     */
    private Integer execNum;

    /**
     * 最后一次执行时间
     */
    private Date lastExecTime;

    /**
     * 第一次执行错误信息(全堆栈)
     */
    private String firstExecError;

    /**
     * 最后一次执行错误信息(全堆栈)
     */
    private String lastExecError;

    /**
     * 调用策略
     */
    private ExecModel execModel;

    /**
     * 多久后重试(毫秒)
     * 第一次延迟时间
     */
    private Long delay;

    /**
     * 执行状态
     */
    private ExecStatus execStatus;

    /**
     * 步长(毫秒)
     */
    private Long step;

    /**
     * N次幂 - 左位移 位数
     * com.lord.otter.infrastructure.retry.constant.ExecModel#POW_STEP时生效
     */
    private Integer pow;

    /**
     * 业务组
     */
    private String businessGroup;

    /**
     * 关联的业务id
     */
    private String businessId;

    /**
     * 最大重试次数
     */
    private Integer maxRetryNumber;

    /**
     * 创建时间
     */
    private Date createTime;

    /**
     * 成功回调的bean名称
     */
    private String callbackBeanName;

    /**
     * 重试信息
     */
    private List<HttpRetryInfo> details;

    public List<HttpRetryInfo> getDetails() {
        if (CollUtil.isEmpty(this.details)) {
            this.details = new ArrayList<>();
        }
        return details;
    }

    /**
     * 新增 任务
     *
     * @param command 添加名称
     * @author lord
     * 2025/8/17 00:11
     */
    public static HttpRetryTask create(AddHttpRetryTaskCommand command) {
        command.check();
        HttpRetryTask domain = new HttpRetryTask();
        domain.setId(IdUtil.getSnowflakeNextId());
        domain.setUrl(command.getUrl());
        domain.setMethod(command.getMethod());
        domain.setRequestBody(command.getRequestBody());
        domain.setHeaders(command.getHeaders());
        domain.setExecNum(0);
        domain.setFirstExecError(command.getFirstExecError());
        domain.setExecModel(command.getExecModel());
        domain.setDelay(command.getDelay());
        domain.setStep(command.getStep());
        domain.setPow(command.getPow());
        domain.setBusinessGroup(command.getBusinessGroup());
        domain.setBusinessId(command.getBusinessId());
        domain.setMaxRetryNumber(command.getMaxRetryNumber());
        domain.setCallbackBeanName(command.getCallbackBeanName());
        domain.setCreateTime(new Date());

        createDetail(domain);
        return domain;
    }

    private static void createDetail(HttpRetryTask domain) {
        // 初始化重试信息列表
        List<HttpRetryInfo> details = domain.getDetails();
        Date currentTime = new Date();
        //下次重试时间
        long nextRetryTime = currentTime.getTime() + domain.getDelay();
        if (ExecModel.FIXED_STEP.equals(domain.getExecModel())) {
            for (int i = 1; i <= domain.getMaxRetryNumber(); i++) {
                HttpRetryInfo retryInfo = HttpRetryInfo.create(new Date(nextRetryTime));
                details.add(retryInfo);
                // 固定步长：每次增加固定的步长时间
                nextRetryTime += domain.getStep();
            }
        } else if (ExecModel.POW_STEP.equals(domain.getExecModel())) {
            long step = domain.getStep();
            for (int i = 1; i <= domain.getMaxRetryNumber(); i++) {
                HttpRetryInfo retryInfo = HttpRetryInfo.create(new Date(nextRetryTime));
                details.add(retryInfo);
                //次幂步长：直接左位移
                step = step << domain.getPow();
                nextRetryTime += step;
            }
        }
    }
}
